package memorphic.logging
{
	import flash.events.Event;
	import flash.system.Capabilities;
	import flash.system.System;
	
	import memorphic.utils.StringTemplate;
	
	import mx.collections.ArrayCollection;
	import mx.logging.AbstractTarget;
	import mx.logging.Log;
	import mx.logging.LogEvent;
	import mx.logging.LogEventLevel;

	public class LogPanelTarget extends AbstractTarget
	{
		
		private static var instance:LogPanelTarget;
		private static const constructorKey:Object = new Object();
		
		private var __logTemplate:StringTemplate = new StringTemplate("[{levelString}] {message}");
		
		
		
		[Bindable]
		private var magicMakeMeEventDispatcher:Boolean = true;
		private var liveLogEvents:ArrayCollection = new ArrayCollection();
		
		private var __filterCategory:String = null;
		private var __filterLevel:int = LogEventLevel.ALL;
		
		
		public static function getInstance():LogPanelTarget
		{
			if(!instance){
				instance = new LogPanelTarget(constructorKey);
			}
			return instance;
		}
		
		public function LogPanelTarget(key:Object)
		{
			if(key != constructorKey){
				throw new ArgumentError("Use getInstance() to instanciate LogPanelTarget");
			}
		}
		
		/**
		 * filterLevel is different from the main log level setting. It only affects what is displayed, while
		 * the main log level setting actually filters the events before they are processed.
		 */
		[Bindable("filterChanged")]
		public function get filterLevel():int
		{
			return __filterLevel;
		}
		public function set filterLevel(lvl:int):void
		{
			__filterLevel = lvl;
			dispatchEvent(new Event("filterChanged"));
		}
		
		[Bindable("templateChanged")]
		public function get logTemplate():StringTemplate
		{
			return __logTemplate;
		}
		public function set logTemplate(tmpl:StringTemplate):void
		{
			__logTemplate = tmpl;
			dispatchEvent(new Event("templateChanged"));
		}
		
		[Bindable("filterChanged")]
		public function get filterCategory():String
		{
			return __filterCategory;
		}
		public function set filterCategory(cat:String):void
		{
			__filterCategory = cat;
			dispatchEvent(new Event("filterChanged"));
		}
		

		[Bindable("filterChanged")]
		[Bindable("log")]		
		[Bindable("templateChanged")]
		[Bindable("refresh")]
		public function get text():String
		{
			var t:String = "";
			var n:int = liveLogEvents.length;
			var logItem:LogItem;
			for(var i:int=0; i<n; i++){
				logItem = liveLogEvents.getItemAt(i)as LogItem;
				if((!__filterCategory || __filterCategory == logItem.category)
					&&(__filterLevel <= logItem.level))
				{	
					t += logTemplate.apply(logItem) + "\r";
				}
			}
			return t;
		}
		
		/**
		 * Same as text, but makes sure that the correct line end characters are used for each operating system
		 */
		[Bindable("filterChanged")]
		[Bindable("log")]		
		[Bindable("templateChanged")]
		[Bindable("refresh")]	
		public function get osText():String
		{
			var t:String = text;
			if(Capabilities.os.indexOf("Windows") > -1){
				return t.replace("\r", "\r\n");
			}else{
				return t;
			}
		}
		
		public override function logEvent(event:LogEvent):void
		{
			liveLogEvents.addItem(LogItem.fromLogEvent(event));
			dispatchEvent(event.clone());
		}
		
		
		public function clearLog():void
		{
			liveLogEvents.removeAll();
			dispatchEvent(new Event("refresh"));
		}
	}
}


import mx.logging.LogEvent;
import mx.logging.ILogger;
import mx.formatters.DateFormatter;
	


class LogItem
{
	public var level:int;
	public var levelString:String;
	
	public var message:String;
	public var category:String;
	
	public var date:Date;
	
	private static const dateFormatter:DateFormatter = new DateFormatter();
	private static const timeFormatter:DateFormatter = new DateFormatter();
	// static
	{
		dateFormatter.formatString = "MM/DD HH:NN:SS";
		timeFormatter.formatString = "HH:NN:SS";
	}

	public function get timeString():String
	{
		return timeFormatter.format(date) + "."+("00" + date.milliseconds).substr(-3);
	}

	public function get dateString():String
	{
		return dateFormatter.format(date);
	}
	
	public function LogItem(level:int, levelString:String, message:String, category:String, date:Date=null)
	{
		this.level = level;
		this.levelString = levelString;
		this.message = message;
		this.category = category;
		this.date = date || new Date();
	}
	
	public static function fromLogEvent(event:LogEvent):LogItem
	{
		var cat:String = null;
		if(event.target is ILogger){
			cat = ILogger(event.target).category;
		}
		return new LogItem(event.level, LogEvent.getLevelString(event.level), event.message, cat);
	}
}